package jfuzzyprincepless.zarchive.puntaelenaexperiments;

import puntaelena.graphsupport.FacilitiesGraph;
import puntaelena.graphsupport.GenerateGrSettings;
import puntaelena.graphsupport.GraphSupportFront;
import puntaelena.graphsupport.solver.IFacilityLocationSolver;

import java.util.ArrayList;
import java.util.List;

import puntaelena.facilitylocation.hillclimbing.solver.HillClimbingSolver;
import puntaelena.facilitylocation.hillclimbing.solver.PEHillClimbingAlgorithmBuilder;

import laboratoryQ.experiment.IExperiment;
import laboratoryQ.experiment.IExperimentResult;
import laboratoryQ.experiment.process.IExperimentContext;

public class GrSzExperiment implements IExperiment {
	@Override
	public IExperimentResult runExperiment(
		IExperimentContext icontext,
		IExpRunStatusHandler expRunStatusHandler
	) {
		GrSzExperimentContext context = (GrSzExperimentContext) icontext;
		FGraphSzDepCompareFactor3vsTrivialExSettings settings =
			context.getExperimentSettings();

		int citiesRange = settings.citiesCountEnd - settings.citiesCountBeg + 1;
		int facilitiesRange = settings.facilitiesCountEnd - settings.facilitiesCountBeg + 1;

		List<double[]> result = new ArrayList<double[]>();
		result.add(new double[citiesRange]);
		result.add(new double[citiesRange]);
		result.add(new double[citiesRange]);
		result.add(new double[citiesRange]);

		List<double[]> diffAgainstAdv = new ArrayList<double[]>();
		diffAgainstAdv.add(new double[citiesRange]);

		IFacilityLocationSolver trivialSolver = GraphSupportFront.getTrivialSolver();
		IFacilityLocationSolver factor3Solver = GraphSupportFront.getFactor3Solver();
		PEHillClimbingAlgorithmBuilder builder = new PEHillClimbingAlgorithmBuilder();
		builder.addForAllCitiesRelinkTheBest();
		builder.addForChpUsflClsdFcltOpenRelikn();
		IFacilityLocationSolver hillClimbSolver = new HillClimbingSolver(factor3Solver, builder.buildConstruction());
		builder.addForFacilitySearchesAndClosesEnemies();
		IFacilityLocationSolver hillClimbSolverAdv = new HillClimbingSolver(factor3Solver, builder.buildConstruction());

		GenerateGrSettings genset = new GenerateGrSettings();
		genset.neighborCountBeg = settings.neighborCountBeg;
		genset.neighborCountRange = settings.neighborCountRange;
		genset.cheapEdgeBeg = settings.cheapEdgeBeg;
		genset.cheapEdgeRange = settings.cheapEdgeRange;
		genset.expensiveEdgeBeg = settings.expensiveEdgeBeg;
		genset.expensiveEdgeRange = settings.expensiveEdgeRange;
		genset.facilityPriceBeg = settings.facilityPriceBeg;
		genset.facilityPriceRange = settings.facilityPriceRange;
		genset.triangleIneqEnabled = settings.triangleIneqEnabled;

		for (int citiesCount = settings.citiesCountBeg; citiesCount <= settings.citiesCountEnd; citiesCount++) {
			genset.randomSeed = context.getRandom().get() * 10000;

			int facilitiesCount = settings.facilitiesCountBeg;
			facilitiesCount += ((double)citiesCount - settings.citiesCountBeg) * facilitiesRange / citiesRange;
			if (citiesCount == settings.citiesCountEnd && facilitiesCount < settings.facilitiesCountEnd) {
				throw new RuntimeException("Unexpected");
			}

			genset.firstBipart = citiesCount;
			genset.secondBipart = facilitiesCount;

			FacilitiesGraph fgraph = (FacilitiesGraph) settings.getGraphGenerator().generate(genset);
			boolean[] isfcltOpen = new boolean[facilitiesCount];
			int[] witnesses = new int[citiesCount];

			trivialSolver.solve(fgraph, isfcltOpen, witnesses);
			double costA = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);// TODO: single names 4 witness - isfcltopen
			result.get(0)[citiesCount - settings.citiesCountBeg] = costA;

			factor3Solver.solve(fgraph, isfcltOpen, witnesses);
			double costB = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
			result.get(1)[citiesCount - settings.citiesCountBeg] = costB;

			hillClimbSolver.solve(fgraph, isfcltOpen, witnesses);
			double costC = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
			result.get(2)[citiesCount - settings.citiesCountBeg] = costC;

			hillClimbSolverAdv.solve(fgraph, isfcltOpen, witnesses);
			double costD = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
			result.get(3)[citiesCount - settings.citiesCountBeg] = costD;
			diffAgainstAdv.get(0)[citiesCount - settings.citiesCountBeg] = costC - costD;

			expRunStatusHandler.stepsProcessed(citiesCount - settings.citiesCountBeg, citiesRange);
		}

		context.graphSizeCosts = result;
		context.diffAgainstAdv = diffAgainstAdv;

		expRunStatusHandler.handleExperimentResult(context);
		return context;
	}
}
